package uk.ac.lkl.common.allocator.ui;



import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import uk.ac.lkl.common.allocator.AllocatorModel; //import uk.ac.lkl.common.allocator.MasterAllocatorModel;
import uk.ac.lkl.common.allocator.SlaveAllocatorModel;
import uk.ac.lkl.common.associator.Associator;
import uk.ac.lkl.common.ui.ContextualisedViewCreator;
import uk.ac.lkl.common.util.Property;

//Question: should classes in lkl.common refernce classes in lkl.lpp?
import uk.ac.lkl.lpp.PedagogyPlan;
import uk.ac.lkl.lpp.model.PedagogyPlanModel;

import uk.ac.lkl.lpp.xml.ParameterMap;
import uk.ac.lkl.lpp.xml.XMLConstructor;
import uk.ac.lkl.lpp.xml.XMLException;

import uk.ac.lkl.lpp.ui.BackCompat;
import uk.ac.lkl.lpp.ui.Messages;


/**
 * 
 * <p>
 * <b>Question:</b> why is this here if it depends on plan? Should abstract
 * away or move it to .lpp .
 * </p>
 * 
 * @author Darren Pearce
 * 
 */
public class SwitchingAllocatorViewXMLConstructor extends XMLConstructor {

    private static final Logger logger = Logger.getLogger("uk.ac.lkl.lpp");


    protected Object createFromParameterMap(PedagogyPlan plan,
                                            ParameterMap parameterMap)
            throws XMLException {
        // Dumps the parameterMap

        String type = parameterMap.getParameter("type");
        // logger.info("**plan:"+plan.getModel().getAllocatorModel(cl));
        if (type.equals("default")) {
            return createDefaultSwitchingAllocatorView(plan, parameterMap);
        }
        else if (type.equals("slave")) {
            return createSlaveSwitchingAllocatorView(plan, parameterMap);
        }
        else {
            throw new XMLException("Unknown type: " + type);
        }
    }


    private Object createDefaultSwitchingAllocatorView(PedagogyPlan plan,
                                                       ParameterMap parameterMap)
            throws XMLException {
        try {

            String allocatorClassName = parameterMap
                    .getParameter("allocatorClass");
            String allocatorListName = parameterMap
                    .getParameterAlways("allocatorListName");
            String viewClassName = parameterMap.getParameter("viewClass");
            String viewCreatorClassName = viewClassName + "ViewCreator";

            // hack to convert pre 0.72 data files
            if (BackCompat.isOldForm(allocatorClassName))
                allocatorClassName = BackCompat.upgrade(allocatorClassName);
            if (BackCompat.isOldForm(viewCreatorClassName))
                viewCreatorClassName = BackCompat.upgrade(viewCreatorClassName);

            Class allocatorClass = Class.forName(allocatorClassName);

            // Returns null for teachingMethod
            AllocatorModel allocatorModel = plan.getModel()
                    .getAllocatorModel(allocatorListName, allocatorClass);

            Class viewCreatorClass = Class.forName(viewCreatorClassName);
            Constructor constructor = viewCreatorClass
                    .getConstructor(PedagogyPlan.class);
            ContextualisedViewCreator viewCreator = (ContextualisedViewCreator) constructor
                    .newInstance(plan);

            // This uses the name of the allocationClass to find the title
            // allocatorModel.setTitle(Messages.getLabel(allocatorClassName));

            allocatorModel.setTitle(Messages.getLabel(allocatorClassName));
            // This is to set the units (Weeks,days,hours)taken from the chunk
            // properties

            allocatorModel.setTimeUnit(" ");
//            allocatorModel.setTimeUnit(plan.getModel().getSubType());
//            plan.getModel().addChangeListener(new SubTypeListener(allocatorModel, plan.getModel()));           

            SwitchingAllocatorView view = new SwitchingAllocatorView(
                    allocatorModel, viewCreator);

            return view;

        }
        catch (InvocationTargetException e) {
            throw new XMLException(e);
        }
        catch (NoSuchMethodException e) {
            throw new XMLException(e);
        }
        catch (ClassNotFoundException e) {
            throw new XMLException(e);
        }
        catch (IllegalAccessException e) {
            throw new XMLException(e);
        }
        catch (InstantiationException e) {
            throw new XMLException(e);
        }
    }


    /**
     * 
     * @param plan
     * @param parameterMap
     * @return
     * @throws XMLException
     */
    private Object createSlaveSwitchingAllocatorView(PedagogyPlan plan,
                                                     ParameterMap parameterMap)
            throws XMLException {

        try {
            logger.entering(
                            "SwitchingAllocatorViewXMLConstructor",
                            "createSlaveSwitchingAllocatorView");
            String masterClassName = parameterMap.getParameter("masterClass");
            String masterListName = parameterMap.getParameterAlways("masterListName");

            String slaveClassName = parameterMap.getParameter("slaveClass");
            String slaveListName = parameterMap.getParameterAlways("slaveListName");

            String annotationClassName = parameterMap
                    .getParameterAlways("annotationClass");
            if (masterClassName == null) {
                logger.warning("masterClassName null, bailing out.");
                return null;
            }
            if (slaveClassName == null) {
                logger.warning("slaveClassName null, bailing out.");
                return null;
            }

            // hack to convert pre 0.72 data files
            if (BackCompat.isOldForm(masterClassName))
                masterClassName = BackCompat.upgrade(masterClassName);
            if (BackCompat.isOldForm(slaveClassName))
                slaveClassName = BackCompat.upgrade(slaveClassName);
            if (BackCompat.isOldForm(annotationClassName))
                annotationClassName = BackCompat.upgrade(annotationClassName);

            Class masterClass = Class.forName(masterClassName);
            logger.fine("masterClass: " + masterClass);
            Class slaveClass = Class.forName(slaveClassName);
            logger.fine("slaveClass: " + slaveClass);

            // annotation can be null (I think)
            Class annotationClass;
            if (annotationClassName == null) {
                annotationClass = null;
                logger.fine("annotationClassName null, no problems.");
            }
            else {
                annotationClass = Class.forName(annotationClassName);
                logger.fine("annotationClassName: " + annotationClassName);
            }

            Associator associator = plan.getModel()
                    .getAssociator(
                                   masterListName,
                                   masterClass,
                                   annotationClass,
                                   slaveListName,
                                   slaveClass);
            if (associator == null) {
                logger.warning("associator null, bailing out. masterClass: "
                        + masterClass + " slaveClass: " + slaveClass);
                return null;
            }

            AllocatorModel masterAllocatorModel = plan.getModel()
                    .getAllocatorModel(masterClass);
            if (masterAllocatorModel == null) {
                logger.warning("masterAllocatorModel null, bailing out.");
                return null;

            }

            String viewClassName = parameterMap.getParameter("viewClass");
            if (BackCompat.isOldForm(viewClassName))
                viewClassName = BackCompat.upgrade(viewClassName);

            String viewCreatorClassName = viewClassName + "ViewCreator";

            SlaveAllocatorModel slaveAllocatorModel = new SlaveAllocatorModel(
                    masterAllocatorModel, associator);

            Class viewCreatorClass = Class.forName(viewCreatorClassName);
            Constructor constructor = viewCreatorClass
                    .getConstructor(PedagogyPlan.class);
            ContextualisedViewCreator viewCreator = (ContextualisedViewCreator) constructor
                    .newInstance(plan);

            // This uses the name of the slave to find the title (for the
            // default it takes the allocationClass
            slaveAllocatorModel.setTitle(Messages.getLabel(slaveClassName));

            // This is to set the units (Weeks,days,hours)taken from the chunk
            // properties
            slaveAllocatorModel.setTimeUnit(plan.getModel().getTime_unit());

/*            Property prop = plan.getModel().getProperty("time-unit");
            if (prop == null) {
                slaveAllocatorModel.setTimeUnit("Period");
            }
            else {
                slaveAllocatorModel.setTimeUnit(prop.getValue());
            }
*/
            SwitchingAllocatorView view = new SwitchingAllocatorView(
                    slaveAllocatorModel, viewCreator, false);


            plan.getModel().addAllocatorModel(slaveListName, slaveClass, slaveAllocatorModel); 

            view.setController(new NullAllocatorController());

            return view;
        }
        catch (NoSuchMethodException e) {
            throw new XMLException(e);
        }
        catch (InvocationTargetException e) {
            throw new XMLException(e);
        }
        catch (ClassNotFoundException e) {
            throw new XMLException(e);
        }
        catch (IllegalAccessException e) {
            throw new XMLException(e);
        }
        catch (InstantiationException e) {
            throw new XMLException(e);
        }
    }


    protected void getParametersFromData(PedagogyPlan plan,
                                         ParameterMap parameterMap) {
        // String type =
        // parameterMap.setParameter("type", type);
    }

    private final class SubTypeListener implements ChangeListener {
    	AllocatorModel    aModel;
    	PedagogyPlanModel pModel;
    	
    	public SubTypeListener (AllocatorModel aModel, PedagogyPlanModel pModel){
    		this.aModel = aModel;
    		this.pModel = pModel;
    	}
    	
       	public void stateChanged(ChangeEvent e){
    		aModel.setTimeUnit(pModel.getSubType());
    	}
    }
}
